﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace TfsBacklogMgr.Main
{
    [NotifyPropertyChanged]
    public class Iteration : ViewModelBase, IComparable
    {
        #region Factory

        protected static Dictionary<string, Iteration> _cache = new Dictionary<string, Iteration>();
        protected static Iteration _backlogRoot = null;

        public static Iteration GetBacklogRoot()
        {
            if (_backlogRoot == null)
            {
                _backlogRoot = new Iteration("");
                _backlogRoot._IsBacklogIteration = true;
                // _backlogRoot._SubIters.Add( GetIteration("$") );
                _backlogRoot._BacklogChildren.Add( GetIteration("$") );
                _backlogRoot._ParentPath = "";
                var parent = _backlogRoot.Parent;
            }
            return _backlogRoot;
        }

        public static Iteration GetIteration(string path)
        {
            if (string.IsNullOrWhiteSpace(path))
                return null;

            if (_cache.ContainsKey(path))
                return _cache[path];
            else
            {
                Iteration iter = new Iteration(path);
                _cache[path] = iter;
                // tickle parent property to force recursive call
                var parent = iter.Parent;

                return iter;
            }
        }

        public static void Add(Iteration newIter)
        {
            if (newIter == null)
                return;

            if (!_cache.ContainsKey(newIter.Path))
                _cache[newIter.Path] = newIter;
            else
            {
                _cache.Remove(newIter.Path);
                _cache[newIter.Path] = newIter;
            }
        }

        public static ObservableCollection<Iteration> GetIterations(string path)
        {
            Iteration item = GetIteration(path);
            if (item != null)
                return item.SubIterations;
            else
                return new ObservableCollection<Iteration>();
        }

        public static List<Iteration> GetNonLeafIterations()
        {
            List<Iteration> result = new List<Iteration>();
            foreach (Iteration iter in _cache.Values)
            {
                if (iter.SubIterations != null &&
                    iter.SubIterations.Count > 0)
                    result.Add(iter);

            }
            result.Sort();
            return result;
        }

        public static List<Iteration> GetAllIterations()
        {
            var result = _cache.Values.ToList();
            result.Sort();
            return result;
        }

        #endregion


        private string _Path;
        private string _ParentPath;
        private string _Name;
        private bool _IsBacklogIteration;
        private ObservableCollection<Iteration> _SubIters;
        private ObservableCollection<WorkItem> _WorkItems;
        private ObservableCollection<ViewModelBase> _SprintChildren;
        private ObservableCollection<ViewModelBase> _BacklogChildren;

 
        public string Path
        {
            get
            {
                return _Path;
            }
        }

        public string TfsPath
        {
            get
            {
                string tfsPath = _Path.Replace("$", Context.GetContext().TfsProject);
                return tfsPath;
            }
        }

        public string ShortPath
        {
            get
            {
                if (Parent != this  && Parent != GetBacklogRoot() )
                    return Parent.Name + "/" + Name;
                else
                    return Name;
            }
        }
        public string Name
        {
            get
            {
                return _Name;
            }
        }
        public Iteration Parent
        {
            get
            {
                Iteration parent = GetIteration(_ParentPath);
                if (parent != null && parent != this)
                    parent.AddSubIter(this);
                return parent;
            }
        }

        public bool IsBacklogIteration
        {
            get { return _IsBacklogIteration; }
            set {
                Parent.RemoveSubIter(this);
                _IsBacklogIteration = value;
                if (Parent != this)
                    Parent.AddSubIter(this);
            }
        }

        /// <summary>
        /// Pure backlog iterations have only backlog iters as children - these should not be shown in the sprint iteration list
        /// </summary>
        public bool IsPureBacklogIteration
        {
            get
            {
                if( _IsBacklogIteration )
                {
                    foreach( var iter in _SubIters )
                    {
                        if (iter != this)                       // recursion guard
                        {
                            if (!iter.IsBacklogIteration)
                                return false;
                            if (!iter.IsPureBacklogIteration)   // recursion
                                return false;
                        }
                    }
                }
                return _IsBacklogIteration;
            }
        }


        public ObservableCollection<Iteration> SubIterations
        {
            get
            {
                return _SubIters;
            }
        }
        public ObservableCollection<WorkItem> WorkItems
        {
            get
            {
                return _WorkItems;
            }
        }

        public ObservableCollection<ViewModelBase> SprintChildren
        {
            get
            {
                return _SprintChildren;
            }
        }

        public ObservableCollection<ViewModelBase> BacklogChildren
        {
            get
            {
                //if (_BacklogChildren.Count == 0)
                //    return _SprintChildren;
                return _BacklogChildren;
            }
        }

        public double TotalPoints
        {
            get
            {
                double sum = 0;
                foreach (var item in _WorkItems)
                    sum += item.Points;
                foreach (var iter in _SubIters)
                    sum += iter.TotalPoints;
                return sum;
            }
        }

        public double TotalHours
        {
            get
            {
                double sum = 0;
                foreach (var item in _WorkItems)
                    sum += item.Hours;
                foreach (var iter in _SubIters)
                    sum += iter.TotalHours;
                return sum;
            }
        }

        public double TotalRemain
        {
            get
            {
                double sum = 0;
                foreach (var item in _WorkItems)
                    sum += item.Remain;
                foreach (var iter in _SubIters)
                    sum += iter.TotalRemain;
                return sum;
            }
        }

        public System.Windows.Visibility PointsVisibility { get { return Context.GetContext().WITF_Points.Length > 0 ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; } }
        public System.Windows.Visibility HoursVisibility { get { return Context.GetContext().WITF_Hours.Length > 0 ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; } }
        public System.Windows.Visibility RemainVisibility { get { return Context.GetContext().WITF_Remain.Length > 0 ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed; } }


        private void AddSubIter(Iteration child)
        {
            if (!_SubIters.Contains(child) && child != this)
            {
                _SubIters.Add(child);
                if (child.IsPureBacklogIteration)
                {
                    _BacklogChildren.Add(child);
                }
                else
                if( child.IsBacklogIteration )
                {
                    _SprintChildren.Add(child);
                    _BacklogChildren.Add(child);
                }
                else
                {
                    _SprintChildren.Add(child);
                }
                //child.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(child_PropertyChanged);
            }
        }

        private void RemoveSubIter(Iteration child)
        {
            if (_SubIters.Contains(child))
            {
                _SubIters.Remove(child);
                _SprintChildren.Remove(child);
                _BacklogChildren.Remove(child);
            }
        }

        void child_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "TotalPoints") OnPropertyChanged("TotalPoints");
            if (e.PropertyName == "TotalHours") OnPropertyChanged("TotalHours");
            if (e.PropertyName == "TotalRemain") OnPropertyChanged("TotalRemain");
        }

        internal void UpdateTotals()
        {
            OnPropertyChanged("TotalPoints");
            OnPropertyChanged("TotalHours");
            OnPropertyChanged("TotalRemain");
            //if (Parent != null && Parent != this)
            //    Parent.UpdateTotals();
        }

        public Iteration(string Path)
        {
            _SubIters = new ObservableCollection<Iteration>();
            _WorkItems = new ObservableCollection<WorkItem>();
            _SprintChildren = new ObservableCollection<ViewModelBase>();
            _BacklogChildren = new ObservableCollection<ViewModelBase>();

            _Path = Path;
            _Name = _Path;
            string[] parts = _Path.Split('/', '\\');
            if( parts.Length > 1 )
                _ParentPath = string.Join("\\", parts, 0, parts.Length - 1);
            else
                _ParentPath = parts[0];
            _Name = parts[parts.Length - 1];
        }

        internal void AddWorkItem(WorkItem workItem)
        {
            // do not add work item if the workitem is a sub-item of an item already in this iteration.
            if (workItem != null && ! _WorkItems.Contains(workItem) && ( workItem.Parent == null  || workItem.Parent.Iteration != this ) )
            {
                _WorkItems.Add(workItem);
                if (IsBacklogIteration)
                    _BacklogChildren.Add(workItem);
                else
                    _SprintChildren.Add(workItem);
                //workItem.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(workItem_PropertyChanged);
                RemoveChildrenOfWorkItem(workItem);
                UpdateTotals();
            }
        }

        internal void RemoveChildrenOfWorkItem(WorkItem workItem)
        {
            if (workItem != null)
            {
                foreach (var wi in workItem.Children)
                {
                    RemoveWorkItem(wi);
                }
            }
        }

        internal void RemoveWorkItem(WorkItem workItem)
        {
            if (workItem != null)
            {
                _WorkItems.Remove(workItem);
                _SprintChildren.Remove(workItem);
                _BacklogChildren.Remove(workItem);
                UpdateTotals();
            }
        }

        void workItem_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Points") OnPropertyChanged("TotalPoints");
            if (e.PropertyName == "Hours") OnPropertyChanged("TotalHours");
            if (e.PropertyName == "Remain") OnPropertyChanged("TotalRemain");
        }

        public override string ToString()
        {
            return _Path;
        }


        public int CompareTo(object obj)
        {
            int result = 1;
            Iteration i = obj as Iteration;
            if( i != null )
                result = this.Path.CompareTo(i.Path);
            return result;

        }

    }
}
